﻿using NWaves.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace NWaves.Transforms.Wavelets
{
    /// <summary>
    /// Represents wavelet.
    /// </summary>
    public class Wavelet
    {
        /// <summary>
        /// Gets wavelet name.
        /// </summary>
        public string Name { get; protected set; }

        /// <summary>
        /// Gets wavelet length.
        /// </summary>
        public int Length { get; protected set; }

        /// <summary>
        /// Gets LP coefficients for decomposition.
        /// </summary>
        public float[] LoD { get; protected set; }

        /// <summary>
        /// Gets HP coefficients for decomposition.
        /// </summary>
        public float[] HiD { get; protected set; }

        /// <summary>
        /// Gets LP coefficients for reconstruction.
        /// </summary>
        public float[] LoR { get; protected set; }

        /// <summary>
        /// Gets HP coefficients for reconstruction.
        /// </summary>
        public float[] HiR { get; protected set; }

        /// <summary>
        /// Constructs wavelet from <paramref name="waveletFamily"/> and number of <paramref name="taps"/>.
        /// </summary>
        /// <param name="waveletFamily">Wavelet family</param>
        /// <param name="taps">Number of taps</param>
        public Wavelet(WaveletFamily waveletFamily, int taps = 1)
        {
            MakeWavelet(waveletFamily, taps);
        }

        /// <summary>
        /// Constructs wavelet from <paramref name="name"/>. 
        /// Supported names: "haar", "db1".."db20", "sym2".."sym20", "coif1".."coif5".
        /// </summary>
        /// <param name="name">Wavelet name</param>
        public Wavelet(string name)
        {
            WaveletFamily waveletFamily;
            int taps = 1;

            name = name.ToLower();

            if (name == "haar")
            {
                waveletFamily = WaveletFamily.Haar;
            }
            else
            {
                var digitPos = -1;
                for (var i = 0; i < name.Length; i++)
                {
                    if (char.IsDigit(name[i]))
                    {
                        digitPos = i;
                        break;
                    }
                }

                var wname = name;

                if (digitPos < 0)
                {
                    taps = 1;
                }
                else
                {
                    wname = name.Substring(0, digitPos);
                    taps = int.Parse(name.Substring(digitPos));
                }

                switch (wname)
                {
                    case "db":
                        waveletFamily = WaveletFamily.Daubechies;
                        break;
                    case "sym":
                        waveletFamily = WaveletFamily.Symlet;
                        break;
                    case "coif":
                        waveletFamily = WaveletFamily.Coiflet;
                        break;
                    default:
                        throw new ArgumentException($"Unrecognized wavelet name: {name}");
                }
            }

            MakeWavelet(waveletFamily, taps);
        }

        /// <summary>
        /// Constructs wavelet from wavelet coefficients (perhaps, calculated in external software).
        /// </summary>
        /// <param name="loD">LP coefficients for decomposition</param>
        /// <param name="hiD">HP coefficients for decomposition</param>
        /// <param name="loR">LP coefficients for reconstruction</param>
        /// <param name="hiR">HP coefficients for reconstruction</param>
        public Wavelet(IEnumerable<float> loD, IEnumerable<float> hiD, IEnumerable<float> loR, IEnumerable<float> hiR)
        {
            LoD = loD.ToArray();
            HiD = hiD.ToArray();
            LoR = loR.ToArray();
            HiR = hiR.ToArray();

            Guard.AgainstInequality(LoD.Length, HiD.Length, "LP coeffs for decomposition", "HP coeffs for decomposition");
            Guard.AgainstInequality(LoD.Length, LoR.Length, "LP coeffs for decomposition", "LP coeffs for reconstruction");
            Guard.AgainstInequality(LoD.Length, HiR.Length, "LP coeffs for decomposition", "HP coeffs for reconstruction");

            Name = "custom";
            Length = LoD.Length;
        }

        /// <summary>
        /// Fills wavelet fields: name, length and coefficients.
        /// </summary>
        /// <param name="waveletFamily">Wavelet family</param>
        /// <param name="taps">Number of taps</param>
        private void MakeWavelet(WaveletFamily waveletFamily, int taps)
        {
            switch (waveletFamily)
            {
                case WaveletFamily.Daubechies:
                    MakeDaubechiesWavelet(taps);
                    break;
                case WaveletFamily.Symlet:
                    MakeSymletWavelet(taps);
                    break;
                case WaveletFamily.Coiflet:
                    MakeCoifletWavelet(taps);
                    break;
                default:
                    MakeHaarWavelet();
                    break;
            }

            ComputeOrthonormalCoeffs();
        }

        /// <summary>
        /// Computes orthonormal coefficients (from LoD coefficients only).
        /// </summary>
        public void ComputeOrthonormalCoeffs()
        {
            HiD = LoD.Reverse().ToArray();

            for (var i = 0; i < HiD.Length; i += 2)
            {
                HiD[i] = -HiD[i];
            }

            LoR = LoD.Reverse().ToArray();
            HiR = HiD.Reverse().ToArray();
        }


        #region wavelet coefficients

        /// <summary>
        /// Setups Haar wavelet.
        /// </summary>
        protected void MakeHaarWavelet()
        {
            Name = "haar";
            Length = 2;

            var sqrt2 = (float)Math.Sqrt(2);

            LoD = new[] { 1 / sqrt2, 1 / sqrt2 };
        }

        /// <summary>
        /// Setups Daubechies wavelet.
        /// </summary>
        /// <param name="taps">Number of taps</param>
        protected void MakeDaubechiesWavelet(int taps)
        {
            Name = $"db{taps}";
            Length = 2 * taps;

            switch (taps)
            {
                case 1:
                    var sqrt2 = (float)Math.Sqrt(2);            // just like Haar
                    LoD = new[] { 1 / sqrt2, 1 / sqrt2 };
                    break;
                case 2:
                    LoD = new[] { -0.12940952255092145f,
                                   0.22414386804185735f,
                                   0.836516303737469f,
                                   0.48296291314469025f };
                    break;
                case 3:
                    LoD = new[] { 0.035226291882100656f,
                                 -0.08544127388224149f,
                                 -0.13501102001039084f,
                                  0.4598775021193313f,
                                  0.8068915093133388f,
                                  0.3326705529509569f };
                    break;
                case 4:
                    LoD = new[] { -0.010597401784997278f,
                                   0.032883011666982945f,
                                   0.030841381835986965f,
                                  -0.18703481171888114f,
                                  -0.02798376941698385f,
                                   0.6308807679295904f,
                                   0.7148465705525415f,
                                   0.23037781330885523f };
                    break;
                case 5:
                    LoD = new[] { 0.003335725285001549f,
                                 -0.012580751999015526f,
                                 -0.006241490213011705f,
                                  0.07757149384006515f,
                                 -0.03224486958502952f,
                                 -0.24229488706619015f,
                                  0.13842814590110342f,
                                  0.7243085284385744f,
                                  0.6038292697974729f,
                                  0.160102397974125f };
                    break;
                case 6:
                    LoD = new[] { -0.00107730108499558f,
                                   0.004777257511010651f,
                                   0.0005538422009938016f,
                                  -0.031582039318031156f,
                                   0.02752286553001629f,
                                   0.09750160558707936f,
                                  -0.12976686756709563f,
                                  -0.22626469396516913f,
                                   0.3152503517092432f,
                                   0.7511339080215775f,
                                   0.4946238903983854f,
                                   0.11154074335008017f };
                    break;
                case 7:
                    LoD = new[] { 0.0003537138000010399f,
                                 -0.0018016407039998328f,
                                  0.00042957797300470274f,
                                  0.012550998556013784f,
                                 -0.01657454163101562f,
                                 -0.03802993693503463f,
                                  0.0806126091510659f,
                                  0.07130921926705004f,
                                 -0.22403618499416572f,
                                 -0.14390600392910627f,
                                  0.4697822874053586f,
                                  0.7291320908465551f,
                                  0.39653931948230575f,
                                  0.07785205408506236f };
                    break;
                case 8:
                    LoD = new[] { -0.00011747678400228192f,
                                   0.0006754494059985568f,
                                  -0.0003917403729959771f,
                                  -0.00487035299301066f,
                                   0.008746094047015655f,
                                   0.013981027917015516f,
                                  -0.04408825393106472f,
                                  -0.01736930100202211f,
                                   0.128747426620186f,
                                   0.00047248457399797254f,
                                  -0.2840155429624281f,
                                  -0.015829105256023893f,
                                   0.5853546836548691f,
                                   0.6756307362980128f,
                                   0.3128715909144659f,
                                   0.05441584224308161f };
                    break;
                case 9:
                    LoD = new[] { 3.9347319995026124e-05f,
                                 -0.0002519631889981789f,
                                  0.00023038576399541288f,
                                  0.0018476468829611268f,
                                 -0.004281503681904723f,
                                 -0.004723204757894831f,
                                  0.022361662123515244f,
                                  0.00025094711499193845f,
                                 -0.06763282905952399f,
                                  0.030725681478322865f,
                                  0.14854074933476008f,
                                 -0.09684078322087904f,
                                 -0.29327378327258685f,
                                  0.13319738582208895f,
                                  0.6572880780366389f,
                                  0.6048231236767786f,
                                  0.24383467463766728f,
                                  0.03807794736316728f };
                    break;
                case 10:
                    LoD = new[] { -1.326420300235487e-05f,
                                   9.358867000108985e-05f,
                                  -0.0001164668549943862f,
                                  -0.0006858566950046825f,
                                   0.00199240529499085f,
                                   0.0013953517469940798f,
                                  -0.010733175482979604f,
                                   0.0036065535669883944f,
                                   0.03321267405893324f,
                                  -0.02945753682194567f,
                                  -0.07139414716586077f,
                                   0.09305736460380659f,
                                   0.12736934033574265f,
                                  -0.19594627437659665f,
                                  -0.24984642432648865f,
                                   0.2811723436604265f,
                                   0.6884590394525921f,
                                   0.5272011889309198f,
                                   0.18817680007762133f,
                                   0.026670057900950818f };
                    break;
                case 11:
                    LoD = new[] { 4.494274277236352e-06f,
                                 -3.463498418698379e-05f,
                                  5.443907469936638e-05f,
                                  0.00024915252355281426f,
                                 -0.0008930232506662366f,
                                 -0.00030859285881515924f,
                                  0.004928417656058778f,
                                 -0.0033408588730145018f,
                                 -0.015364820906201324f,
                                  0.02084090436018004f,
                                  0.03133509021904531f,
                                 -0.06643878569502022f,
                                 -0.04647995511667613f,
                                  0.14981201246638268f,
                                  0.06604358819669089f,
                                 -0.27423084681792875f,
                                 -0.16227524502747828f,
                                  0.41196436894789695f,
                                  0.6856867749161785f,
                                  0.44989976435603013f,
                                  0.1440670211506196f,
                                  0.01869429776147044f };
                    break;
                case 12:
                    LoD = new[] { -1.5290717580684923e-06f,
                                   1.2776952219379579e-05f,
                                  -2.4241545757030318e-05f,
                                  -8.850410920820318e-05f,
                                   0.0003886530628209267f,
                                   6.5451282125215034e-06f,
                                  -0.0021795036186277044f,
                                   0.0022486072409952287f,
                                   0.006711499008795549f,
                                  -0.012840825198299882f,
                                  -0.01221864906974642f,
                                   0.04154627749508764f,
                                   0.010849130255828966f,
                                  -0.09643212009649671f,
                                   0.0053595696743599965f,
                                   0.18247860592758275f,
                                  -0.023779257256064865f,
                                  -0.31617845375277914f,
                                  -0.04476388565377762f,
                                   0.5158864784278007f,
                                   0.6571987225792911f,
                                   0.3773551352142041f,
                                   0.10956627282118277f,
                                   0.013112257957229239f };
                    break;
                case 13:
                    LoD = new[] { 5.2200350984548e-07f,
                                 -4.700416479360808e-06f,
                                  1.0441930571407941e-05f,
                                  3.067853757932436e-05f,
                                 -0.0001651289885565057f,
                                  4.9251525126285676e-05f,
                                  0.000932326130867249f,
                                 -0.0013156739118922766f,
                                 -0.002761911234656831f,
                                  0.007255589401617119f,
                                  0.003923941448795577f,
                                 -0.02383142071032781f,
                                  0.002379972254052227f,
                                  0.056139477100276156f,
                                 -0.026488406475345658f,
                                 -0.10580761818792761f,
                                  0.07294893365678874f,
                                  0.17947607942935084f,
                                 -0.12457673075080665f,
                                 -0.31497290771138414f,
                                  0.086985726179645f,
                                  0.5888895704312119f,
                                  0.6110558511587811f,
                                  0.3119963221604349f,
                                  0.08286124387290195f,
                                  0.009202133538962279f };
                    break;
                case 14:
                    LoD = new[] { -1.7871399683109222e-07f,
                                   1.7249946753674012e-06f,
                                  -4.389704901780418e-06f,
                                  -1.0337209184568496e-05f,
                                   6.875504252695734e-05f,
                                  -4.177724577037067e-05f,
                                  -0.00038683194731287514f,
                                   0.0007080211542354048f,
                                   0.001061691085606874f,
                                  -0.003849638868019787f,
                                  -0.0007462189892638753f,
                                   0.01278949326634007f,
                                  -0.0056150495303375755f,
                                  -0.030185351540353976f,
                                   0.02698140830794797f,
                                   0.05523712625925082f,
                                  -0.0715489555039835f,
                                  -0.0867484115681106f,
                                   0.13998901658445695f,
                                   0.13839521386479153f,
                                  -0.2180335299932165f,
                                  -0.27168855227867705f,
                                   0.21867068775886594f,
                                   0.6311878491047198f,
                                   0.5543056179407709f,
                                   0.25485026779256437f,
                                   0.062364758849384874f,
                                   0.0064611534600864905f };
                    break;
                case 15:
                    LoD = new[] { 6.133359913303714e-08f,
                                 -6.316882325879451e-07f,
                                  1.8112704079399406e-06f,
                                  3.3629871817363823e-06f,
                                 -2.8133296266037558e-05f,
                                  2.579269915531323e-05f,
                                  0.00015589648992055726f,
                                 -0.00035956524436229364f,
                                 -0.0003734823541372647f,
                                  0.0019433239803823459f,
                                 -0.00024175649075894543f,
                                 -0.0064877345603061454f,
                                  0.005101000360422873f,
                                  0.015083918027862582f,
                                 -0.020810050169636805f,
                                 -0.02576700732836694f,
                                  0.054780550584559995f,
                                  0.033877143923563204f,
                                 -0.11112093603713753f,
                                 -0.0396661765557336f,
                                  0.19014671400708816f,
                                  0.06528295284876569f,
                                 -0.28888259656686216f,
                                 -0.19320413960907623f,
                                  0.33900253545462167f,
                                  0.6458131403572103f,
                                  0.4926317717079753f,
                                  0.20602386398692688f,
                                  0.04674339489275062f,
                                  0.004538537361577376f };
                    break;
                case 16:
                    LoD = new[] { -2.1093396300980412e-08f,
                                   2.3087840868545578e-07f,
                                  -7.363656785441815e-07f,
                                  -1.0435713423102517e-06f,
                                   1.133660866126152e-05f,
                                  -1.394566898819319e-05f,
                                  -6.103596621404321e-05f,
                                   0.00017478724522506327f,
                                   0.00011424152003843815f,
                                  -0.0009410217493585433f,
                                   0.00040789698084934395f,
                                   0.00312802338120381f,
                                  -0.0036442796214883506f,
                                  -0.006990014563390751f,
                                   0.013993768859843242f,
                                   0.010297659641009963f,
                                  -0.036888397691556774f,
                                  -0.007588974368642594f,
                                   0.07592423604445779f,
                                  -0.006239722752156254f,
                                  -0.13238830556335474f,
                                   0.027340263752899923f,
                                   0.21119069394696974f,
                                  -0.02791820813292813f,
                                  -0.3270633105274758f,
                                  -0.08975108940236352f,
                                   0.44029025688580486f,
                                   0.6373563320829833f,
                                   0.43031272284545874f,
                                   0.1650642834886438f,
                                   0.03490771432362905f,
                                   0.0031892209253436892f };
                    break;
                case 17:
                    LoD = new[] { 7.26749296856637e-09f,
                                 -8.423948446008154e-08f,
                                  2.9577009333187617e-07f,
                                  3.0165496099963414e-07f,
                                 -4.505942477225963e-06f,
                                  6.990600985081294e-06f,
                                  2.318681379876164e-05f,
                                 -8.204803202458212e-05f,
                                 -2.5610109566546042e-05f,
                                  0.0004394654277689454f,
                                 -0.00032813251941022427f,
                                 -0.001436845304805f,
                                  0.0023012052421511474f,
                                  0.002967996691518064f,
                                 -0.008602921520347815f,
                                 -0.0030429899813869555f,
                                  0.022733676583919053f,
                                 -0.0032709555358783646f,
                                 -0.04692243838937891f,
                                  0.022312336178011833f,
                                  0.08110598665408082f,
                                 -0.05709141963185808f,
                                 -0.12681569177849797f,
                                  0.10113548917744287f,
                                  0.19731058956508457f,
                                 -0.12659975221599248f,
                                 -0.32832074836418546f,
                                  0.027314970403312946f,
                                  0.5183157640572823f,
                                  0.6109966156850273f,
                                  0.3703507241528858f,
                                  0.13121490330791097f,
                                  0.025985393703623173f,
                                  0.00224180700103879f };
                    break;
                case 18:
                    LoD = new[] { -2.507934454941929e-09f,
                                   3.06883586303703e-08f,
                                  -1.1760987670250871e-07f,
                                  -7.691632689865049e-08f,
                                   1.768712983622886e-06f,
                                  -3.3326344788769603e-06f,
                                  -8.520602537423464e-06f,
                                   3.741237880730847e-05f,
                                  -1.535917123021341e-07f,
                                  -0.00019864855231101547f,
                                   0.0002135815619103188f,
                                   0.0006284656829644715f,
                                  -0.0013405962983313922f,
                                  -0.0011187326669886426f,
                                   0.004943343605456594f,
                                   0.00011863003387493042f,
                                  -0.013051480946517112f,
                                   0.006262167954438661f,
                                   0.026670705926689853f,
                                  -0.023733210395336858f,
                                  -0.04452614190225633f,
                                   0.05705124773905827f,
                                   0.0648872162123582f,
                                  -0.10675224665906288f,
                                  -0.09233188415030412f,
                                   0.16708131276294505f,
                                   0.14953397556500755f,
                                  -0.21648093400458224f,
                                  -0.2936540407357981f,
                                   0.14722311196952223f,
                                   0.571801654887122f,
                                   0.5718268077650818f,
                                   0.31467894133619284f,
                                   0.10358846582214751f,
                                   0.01928853172409497f,
                                   0.0015763102184365595f };
                    break;
                case 19:
                    LoD = new[] { 8.666848839034483e-10f,
                                 -1.1164020670405678e-08f,
                                  4.636937775802368e-08f,
                                  1.447088298804088e-08f,
                                 -6.86275565779811e-07f,
                                  1.531931476697877e-06f,
                                  3.0109643163099385e-06f,
                                 -1.664017629722462e-05f,
                                  5.105950487090694e-06f,
                                  8.711270467250443e-05f,
                                 -0.00012460079173506306f,
                                 -0.0002606761356811995f,
                                  0.0007358025205041731f,
                                  0.00034180865344939543f,
                                 -0.002687551800734441f,
                                  0.0007689543592242488f,
                                  0.007040747367080495f,
                                 -0.005866922281112195f,
                                 -0.013988388678695632f,
                                  0.019375549889114482f,
                                  0.021623767409452484f,
                                 -0.04567422627778492f,
                                 -0.026501236250778635f,
                                  0.0869067555554507f,
                                  0.02758435062488713f,
                                 -0.14278569504021468f,
                                 -0.03351854190320226f,
                                  0.21234974330662043f,
                                  0.07465226970806647f,
                                 -0.28583863175723145f,
                                 -0.22809139421653665f,
                                  0.2608949526521201f,
                                  0.6017045491300916f,
                                  0.5244363774668862f,
                                  0.26438843174202237f,
                                  0.08127811326580564f,
                                  0.01428109845082521f,
                                  0.0011086697631864314f };
                    break;
                case 20:
                    LoD = new[] { -2.998836489615753e-10f,
                                   4.05612705554717e-09f,
                                  -1.814843248297622e-08f,
                                   2.0143220235374613e-10f,
                                   2.633924226266962e-07f,
                                  -6.847079596993149e-07f,
                                  -1.0119940100181473e-06f,
                                   7.241248287663791e-06f,
                                  -4.376143862182197e-06f,
                                  -3.710586183390615e-05f,
                                   6.774280828373048e-05f,
                                   0.00010153288973669777f,
                                  -0.0003851047486990061f,
                                  -5.349759844340453e-05f,
                                   0.0013925596193045254f,
                                  -0.0008315621728772474f,
                                  -0.003581494259744107f,
                                   0.00442054238676635f,
                                   0.0067216273018096935f,
                                  -0.013810526137727442f,
                                  -0.008789324924555765f,
                                   0.03229429953011916f,
                                   0.0058746818113949465f,
                                  -0.061722899624668884f,
                                   0.005632246857685454f,
                                   0.10229171917513397f,
                                  -0.024716827337521424f,
                                  -0.1554587507060453f,
                                   0.039850246458519104f,
                                   0.22829105082013823f,
                                  -0.016727088308801888f,
                                  -0.3267868004335376f,
                                  -0.13921208801128787f,
                                   0.36150229873889705f,
                                   0.6104932389378558f,
                                   0.4726961853103315f,
                                   0.21994211355113222f,
                                   0.06342378045900529f,
                                   0.010549394624937735f,
                                   0.0007799536136659112f };
                    break;
                default:
                    throw new ArgumentException("Only db1-db20 are supported");
            }
        }

        /// <summary>
        /// Setups Symlet wavelet.
        /// </summary>
        /// <param name="taps">Number of taps</param>
        protected void MakeSymletWavelet(int taps)
        {
            Name = $"sym{taps}";
            Length = 2 * taps;

            switch (taps)
            {
                case 2:
                    LoD = new[] { -0.12940952255092145f,
                                   0.22414386804185735f,
                                   0.836516303737469f,
                                   0.48296291314469025f };
                    break;
                case 3:
                    LoD = new[] { 0.035226291882100656f,
                                 -0.08544127388224149f,
                                 -0.13501102001039084f,
                                  0.4598775021193313f,
                                  0.8068915093133388f,
                                  0.3326705529509569f };
                    break;
                case 4:
                    LoD = new[] { -0.07576571478927333f,
                                  -0.02963552764599851f,
                                   0.49761866763201545f,
                                   0.8037387518059161f,
                                   0.29785779560527736f,
                                  -0.09921954357684722f,
                                  -0.012603967262037833f,
                                   0.0322231006040427f };
                    break;
                case 5:
                    LoD = new[] { 0.027333068345077982f,
                                  0.029519490925774643f,
                                 -0.039134249302383094f,
                                  0.1993975339773936f,
                                  0.7234076904024206f,
                                  0.6339789634582119f,
                                  0.01660210576452232f,
                                 -0.17532808990845047f,
                                 -0.021101834024758855f,
                                  0.019538882735286728f };
                    break;
                case 6:
                    LoD = new[] { 0.015404109327027373f,
                                  0.0034907120842174702f,
                                 -0.11799011114819057f,
                                 -0.048311742585633f,
                                  0.4910559419267466f,
                                  0.787641141030194f,
                                  0.3379294217276218f,
                                 -0.07263752278646252f,
                                 -0.021060292512300564f,
                                  0.04472490177066578f,
                                  0.0017677118642428036f,
                                 -0.007800708325034148f };
                    break;
                case 7:
                    LoD = new[] { 0.002681814568257878f,
                                 -0.0010473848886829163f,
                                 -0.01263630340325193f,
                                  0.03051551316596357f,
                                  0.0678926935013727f,
                                 -0.049552834937127255f,
                                  0.017441255086855827f,
                                  0.5361019170917628f,
                                  0.767764317003164f,
                                  0.2886296317515146f,
                                 -0.14004724044296152f,
                                 -0.10780823770381774f,
                                  0.004010244871533663f,
                                  0.010268176708511255f };
                    break;
                case 8:
                    LoD = new[] { -0.0033824159510061256f,
                                  -0.0005421323317911481f,
                                   0.03169508781149298f,
                                   0.007607487324917605f,
                                  -0.1432942383508097f,
                                  -0.061273359067658524f,
                                   0.4813596512583722f,
                                   0.7771857517005235f,
                                   0.3644418948353314f,
                                  -0.05194583810770904f,
                                  -0.027219029917056003f,
                                   0.049137179673607506f,
                                   0.003808752013890615f,
                                  -0.01495225833704823f,
                                  -0.0003029205147213668f,
                                   0.0018899503327594609f };
                    break;
                case 9:
                    LoD = new[] { 0.0014009155259146807f,
                                  0.0006197808889855868f,
                                 -0.013271967781817119f,
                                 -0.01152821020767923f,
                                  0.03022487885827568f,
                                  0.0005834627461258068f,
                                 -0.05456895843083407f,
                                  0.238760914607303f,
                                  0.717897082764412f,
                                  0.6173384491409358f,
                                  0.035272488035271894f,
                                 -0.19155083129728512f,
                                 -0.018233770779395985f,
                                  0.06207778930288603f,
                                  0.008859267493400484f,
                                 -0.010264064027633142f,
                                 -0.0004731544986800831f,
                                  0.0010694900329086053f };
                    break;
                case 10:
                    LoD = new[] { 0.0007701598091144901f,
                                  9.563267072289475e-05f,
                                 -0.008641299277022422f,
                                 -0.0014653825813050513f,
                                  0.0459272392310922f,
                                  0.011609893903711381f,
                                 -0.15949427888491757f,
                                 -0.07088053578324385f,
                                  0.47169066693843925f,
                                  0.7695100370211071f,
                                  0.38382676106708546f,
                                 -0.03553674047381755f,
                                 -0.0319900568824278f,
                                  0.04999497207737669f,
                                  0.005764912033581909f,
                                 -0.02035493981231129f,
                                 -0.0008043589320165449f,
                                  0.004593173585311828f,
                                  5.7036083618494284e-05f,
                                 -0.0004593294210046588f };
                    break;
                case 11:
                    LoD = new[] { 0.00017172195069934854f,
                                 -3.8795655736158566e-05f,
                                 -0.0017343662672978692f,
                                  0.0005883527353969915f,
                                  0.00651249567477145f,
                                 -0.009857934828789794f,
                                 -0.024080841595864003f,
                                  0.0370374159788594f,
                                  0.06997679961073414f,
                                 -0.022832651022562687f,
                                  0.09719839445890947f,
                                  0.5720229780100871f,
                                  0.7303435490883957f,
                                  0.23768990904924897f,
                                 -0.2046547944958006f,
                                 -0.1446023437053156f,
                                  0.03526675956446655f,
                                  0.04300019068155228f,
                                 -0.0020034719001093887f,
                                 -0.006389603666454892f,
                                  0.00011053509764272153f,
                                  0.0004892636102619239f };
                    break;
                case 12:
                    LoD = new[] { 0.00011196719424656033f,
                                 -1.1353928041541452e-05f,
                                 -0.0013497557555715387f,
                                  0.00018021409008538188f,
                                  0.007414965517654251f,
                                 -0.0014089092443297553f,
                                 -0.024220722675013445f,
                                  0.0075537806116804775f,
                                  0.04917931829966084f,
                                 -0.03584883073695439f,
                                 -0.022162306170337816f,
                                  0.39888597239022f,
                                  0.7634790977836572f,
                                  0.46274103121927235f,
                                 -0.07833262231634322f,
                                 -0.17037069723886492f,
                                  0.01530174062247884f,
                                  0.05780417944550566f,
                                 -0.0026043910313322326f,
                                 -0.014589836449234145f,
                                  0.00030764779631059454f,
                                  0.002350297614183465f,
                                 -1.8158078862617515e-05f,
                                 -0.0001790665869750869f };
                    break;
                case 13:
                    LoD = new[] { 6.820325263075319e-05f,
                                 -3.573862364868901e-05f,
                                 -0.0011360634389281183f,
                                 -0.0001709428585302221f,
                                  0.0075262253899681f,
                                  0.005296359738725025f,
                                 -0.02021676813338983f,
                                 -0.017211642726299048f,
                                  0.013862497435849205f,
                                 -0.0597506277179437f,
                                 -0.12436246075153011f,
                                  0.19770481877117801f,
                                  0.6957391505614964f,
                                  0.6445643839011856f,
                                  0.11023022302137217f,
                                 -0.14049009311363403f,
                                  0.008819757670420546f,
                                  0.09292603089913712f,
                                  0.017618296880653084f,
                                 -0.020749686325515677f,
                                 -0.0014924472742598532f,
                                  0.0056748537601224395f,
                                  0.00041326119884196064f,
                                 -0.0007213643851362283f,
                                  3.690537342319624e-05f,
                                  7.042986690694402e-05f };
                    break;
                case 14:
                    LoD = new[] { -2.5879090265397886e-05f,
                                   1.1210865808890361e-05f,
                                   0.00039843567297594335f,
                                  -6.286542481477636e-05f,
                                  -0.002579441725933078f,
                                   0.0003664765736601183f,
                                   0.01003769371767227f,
                                  -0.002753774791224071f,
                                  -0.029196217764038187f,
                                   0.004280520499019378f,
                                   0.03743308836285345f,
                                  -0.057634498351326995f,
                                  -0.03531811211497973f,
                                   0.39320152196208885f,
                                   0.7599762419610909f,
                                   0.4753357626342066f,
                                  -0.05811182331771783f,
                                  -0.15999741114652205f,
                                   0.02589858753104667f,
                                   0.06982761636180755f,
                                  -0.002365048836740385f,
                                  -0.019439314263626713f,
                                   0.0010131419871842082f,
                                   0.004532677471945648f,
                                  -7.321421356702399e-05f,
                                  -0.0006057601824664335f,
                                   1.9329016965523917e-05f,
                                   4.4618977991475265e-05f };
                    break;
                case 15:
                    LoD = new[] { 9.712419737963348e-06f,
                                 -7.35966679891947e-06f,
                                 -0.00016066186637495343f,
                                  5.512254785558665e-05f,
                                  0.0010705672194623959f,
                                 -0.0002673164464718057f,
                                 -0.0035901654473726417f,
                                  0.003423450736351241f,
                                  0.01007997708790567f,
                                 -0.01940501143093447f,
                                 -0.03887671687683349f,
                                  0.021937642719753955f,
                                  0.04073547969681068f,
                                 -0.04108266663538248f,
                                  0.11153369514261872f,
                                  0.5786404152150345f,
                                  0.7218430296361812f,
                                  0.2439627054321663f,
                                 -0.1966263587662373f,
                                 -0.1340562984562539f,
                                  0.06839331006048024f,
                                  0.06796982904487918f,
                                 -0.008744788886477952f,
                                 -0.01717125278163873f,
                                  0.0015261382781819983f,
                                  0.003481028737064895f,
                                 -0.00010815440168545525f,
                                 -0.00040216853760293483f,
                                  2.171789015077892e-05f,
                                  2.866070852531808e-05f };
                    break;
                case 16:
                    LoD = new[] { 6.230006701220761e-06f,
                                 -3.113556407621969e-06f,
                                 -0.00010943147929529757f,
                                  2.8078582128442894e-05f,
                                  0.0008523547108047095f,
                                 -0.0001084456223089688f,
                                 -0.0038809122526038786f,
                                  0.0007182119788317892f,
                                  0.012666731659857348f,
                                 -0.0031265171722710075f,
                                 -0.031051202843553064f,
                                  0.004869274404904607f,
                                  0.032333091610663785f,
                                 -0.06698304907021778f,
                                 -0.034574228416972504f,
                                  0.39712293362064416f,
                                  0.7565249878756971f,
                                  0.47534280601152273f,
                                 -0.054040601387606135f,
                                 -0.15959219218520598f,
                                  0.03072113906330156f,
                                  0.07803785290341991f,
                                 -0.003510275068374009f,
                                 -0.024952758046290123f,
                                  0.001359844742484172f,
                                  0.0069377611308027096f,
                                 -0.00022211647621176323f,
                                 -0.0013387206066921965f,
                                  3.656592483348223e-05f,
                                  0.00016545679579108483f,
                                 -5.396483179315242e-06f,
                                 -1.0797982104319795e-05f };
                    break;
                case 17:
                    LoD = new[] { 4.297343327345983e-06f,
                                  2.7801266938414138e-06f,
                                 -6.293702597554192e-05f,
                                 -1.3506383399901165e-05f,
                                  0.0004759963802638669f,
                                 -0.000138642302680455f,
                                 -0.0027416759756816018f,
                                  0.0008567700701915741f,
                                  0.010482366933031529f,
                                 -0.004819212803176148f,
                                 -0.03329138349235933f,
                                  0.01790395221434112f,
                                  0.10475461484223211f,
                                  0.0172711782105185f,
                                 -0.11856693261143636f,
                                  0.1423983504146782f,
                                  0.6507166292045456f,
                                  0.681488995344925f,
                                  0.18053958458111286f,
                                 -0.15507600534974825f,
                                 -0.08607087472073338f,
                                  0.016158808725919346f,
                                 -0.007261634750928767f,
                                 -0.01803889724191924f,
                                  0.009952982523509598f,
                                  0.012396988366648726f,
                                 -0.001905407689852666f,
                                 -0.003932325279797902f,
                                  5.8400428694052584e-05f,
                                  0.0007198270642148971f,
                                  2.520793314082878e-05f,
                                 -7.607124405605129e-05f,
                                 -2.4527163425833e-06f,
                                  3.7912531943321266e-06f };
                    break;
                case 18:
                    LoD = new[] { 2.6126125564836423e-06f,
                                  1.354915761832114e-06f,
                                 -4.5246757874949856e-05f,
                                 -1.4020992577726755e-05f,
                                  0.00039616840638254753f,
                                  7.021273459036268e-05f,
                                 -0.002313871814506099f,
                                 -0.00041152110923597756f,
                                  0.009502164390962365f,
                                  0.001642986397278216f,
                                 -0.030325091089369604f,
                                 -0.005077085160757053f,
                                  0.08421992997038655f,
                                  0.03399566710394736f,
                                 -0.15993814866932407f,
                                 -0.052029158983952786f,
                                  0.47396905989393956f,
                                  0.7536291401017928f,
                                  0.40148386057061813f,
                                 -0.032480573290138676f,
                                 -0.07379920729060717f,
                                  0.028529597039037808f,
                                  0.006277944554311694f,
                                 -0.03171268473181454f,
                                 -0.0032607442000749834f,
                                  0.015012356344250213f,
                                  0.001087784789595693f,
                                 -0.005239789683026608f,
                                 -0.00018877623940755607f,
                                  0.0014280863270832796f,
                                  4.741614518373667e-05f,
                                 -0.0002658301102424104f,
                                 -9.858816030140058e-06f,
                                  2.955743762093081e-05f,
                                  7.847298055831765e-07f,
                                 -1.5131530692371587e-06f };
                    break;
                case 19:
                    LoD = new[] { 5.487732768215838e-07f,
                                 -6.463651303345963e-07f,
                                 -1.1880518269823984e-05f,
                                  8.873312173729286e-06f,
                                  0.0001155392333357879f,
                                 -4.612039600210587e-05f,
                                 -0.000635764515004334f,
                                  0.00015915804768084938f,
                                  0.0021214250281823303f,
                                 -0.0011607032572062486f,
                                 -0.005122205002583014f,
                                  0.007968438320613306f,
                                  0.01579743929567463f,
                                 -0.02265199337824595f,
                                 -0.046635983534938946f,
                                  0.0070155738571741596f,
                                  0.008954591173043624f,
                                 -0.06752505804029409f,
                                  0.10902582508127781f,
                                  0.578144945338605f,
                                  0.7195555257163943f,
                                  0.2582661692372836f,
                                 -0.17659686625203097f,
                                 -0.11624173010739675f,
                                  0.09363084341589714f,
                                  0.08407267627924504f,
                                 -0.016908234861345205f,
                                 -0.02770989693131125f,
                                  0.004319351874894969f,
                                  0.008262236955528255f,
                                 -0.0006179223277983108f,
                                 -0.0017049602611649971f,
                                  0.00012930767650701415f,
                                  0.0002762187768573407f,
                                 -1.6821387029373716e-05f,
                                 -2.8151138661550245e-05f,
                                  2.0623170632395688e-06f,
                                  1.7509367995348687e-06f };
                    break;
                case 20:
                    LoD = new[] { 3.695537474835221e-07f,
                                 -1.9015675890554106e-07f,
                                 -7.919361411976999e-06f,
                                  3.025666062736966e-06f,
                                  7.992967835772481e-05f,
                                 -1.928412300645204e-05f,
                                 -0.0004947310915672655f,
                                  7.215991188074035e-05f,
                                  0.002088994708190198f,
                                 -0.0003052628317957281f,
                                 -0.006606585799088861f,
                                  0.0014230873594621453f,
                                  0.01700404902339034f,
                                 -0.003313857383623359f,
                                 -0.031629437144957966f,
                                  0.008123228356009682f,
                                  0.025579349509413946f,
                                 -0.07899434492839816f,
                                 -0.02981936888033373f,
                                  0.4058314443484506f,
                                  0.75116272842273f,
                                  0.47199147510148703f,
                                 -0.0510883429210674f,
                                 -0.16057829841525254f,
                                  0.03625095165393308f,
                                  0.08891966802819956f,
                                 -0.0068437019650692274f,
                                 -0.035373336756604236f,
                                  0.0019385970672402002f,
                                  0.012157040948785737f,
                                 -0.0006111263857992088f,
                                 -0.0034716478028440734f,
                                  0.0001254409172306726f,
                                  0.0007476108597820572f,
                                 -2.6615550335516086e-05f,
                                 -0.00011739133516291466f,
                                  4.525422209151636e-06f,
                                  1.22872527779612e-05f,
                                 -3.2567026420174407e-07f,
                                 -6.329129044776395e-07f };
                    break;
                default:
                    throw new ArgumentException("Only sym2-sym20 are supported");
            }
        }

        /// <summary>
        /// Setups Coiflet wavelet.
        /// </summary>
        /// <param name="taps">Number of taps</param>
        protected void MakeCoifletWavelet(int taps)
        {
            Name = $"coif{taps}";
            Length = 6 * taps;

            switch (taps)
            {
                case 1:
                    LoD = new[] { -0.01565572813546454f,
                                  -0.0727326195128539f,
                                   0.38486484686420286f,
                                   0.8525720202122554f,
                                   0.3378976624578092f,
                                  -0.0727326195128539f };
                    break;
                case 2:
                    LoD = new[] { -0.0007205494453645122f,
                                  -0.0018232088707029932f,
                                   0.0056114348193944995f,
                                   0.023680171946334084f,
                                  -0.0594344186464569f,
                                  -0.0764885990783064f,
                                   0.41700518442169254f,
                                   0.8127236354455423f,
                                   0.3861100668211622f,
                                  -0.06737255472196302f,
                                  -0.04146493678175915f,
                                   0.016387336463522112f };
                    break;
                case 3:
                    LoD = new[] { -3.459977283621256e-05f,
                                  -7.098330313814125e-05f,
                                   0.0004662169601128863f,
                                   0.0011175187708906016f,
                                  -0.0025745176887502236f,
                                  -0.00900797613666158f,
                                   0.015880544863615904f,
                                   0.03455502757306163f,
                                  -0.08230192710688598f,
                                  -0.07179982161931202f,
                                   0.42848347637761874f,
                                   0.7937772226256206f,
                                   0.4051769024096169f,
                                  -0.06112339000267287f,
                                  -0.0657719112818555f,
                                   0.023452696141836267f,
                                   0.007782596427325418f,
                                  -0.003793512864491014f };
                    break;
                case 4:
                    LoD = new[] { -1.7849850030882614e-06f,
                                  -3.2596802368833675e-06f,
                                   3.1229875865345646e-05f,
                                   6.233903446100713e-05f,
                                  -0.00025997455248771324f,
                                  -0.0005890207562443383f,
                                   0.0012665619292989445f,
                                   0.003751436157278457f,
                                  -0.00565828668661072f,
                                  -0.015211731527946259f,
                                   0.025082261844864097f,
                                   0.03933442712333749f,
                                  -0.09622044203398798f,
                                  -0.06662747426342504f,
                                   0.4343860564914685f,
                                   0.782238930920499f,
                                   0.41530840703043026f,
                                  -0.05607731331675481f,
                                  -0.08126669968087875f,
                                   0.026682300156053072f,
                                   0.016068943964776348f,
                                  -0.0073461663276420935f,
                                  -0.0016294920126017326f,
                                   0.0008923136685823146f };
                    break;
                case 5:
                    LoD = new[] { -9.517657273819165e-08f,
                                  -1.6744288576823017e-07f,
                                   2.0637618513646814e-06f,
                                   3.7346551751414047e-06f,
                                  -2.1315026809955787e-05f,
                                  -4.134043227251251e-05f,
                                   0.00014054114970203437f,
                                   0.00030225958181306315f,
                                  -0.0006381313430451114f,
                                  -0.0016628637020130838f,
                                   0.0024333732126576722f,
                                   0.006764185448053083f,
                                  -0.009164231162481846f,
                                  -0.01976177894257264f,
                                   0.03268357426711183f,
                                   0.0412892087501817f,
                                  -0.10557420870333893f,
                                  -0.06203596396290357f,
                                   0.4379916261718371f,
                                   0.7742896036529562f,
                                   0.4215662066908515f,
                                  -0.05204316317624377f,
                                  -0.09192001055969624f,
                                   0.02816802897093635f,
                                   0.023408156785839195f,
                                  -0.010131117519849788f,
                                  -0.004159358781386048f,
                                   0.0021782363581090178f,
                                   0.00035858968789573785f,
                                  -0.00021208083980379827f };
                    break;
                default:
                    throw new ArgumentException("Only coif1-coif5 are supported");
            }
        }

        #endregion
    }
}
